/**************************************************************************************************
* Title: SimulationManager.cpp
* Authors: Gael Huber, Erin Mooney
* Description: Manages the actual game
**************************************************************************************************/
#include "SimulationManager.h"

// Singleton instance
template<> SimulationManager* Singleton<SimulationManager>::singleton = 0;

// Constructor
SimulationManager::SimulationManager(void) {
}	

// Destructor
SimulationManager::~SimulationManager(void) {
}

// Initialize
void SimulationManager::startUp(void) {
	// random seed
	srand((unsigned int) time(0));

	// Define startPos
	startPos = Vector3(0.0f, 0.5f, 0.0f);

	// create the input manager
	inputManager = new InputManager();
	
	// create the render manager
	renderManager = new RenderManager();
	renderManager->startUp();

	// create physics manager
	physicsManager = new PhysicsManager();
	physicsManager->startUp();

	// Create the bar
	createBar();

	// Create the spring
	createSpring();

	// Create the pivot
	createPivot();

	// Make sure the scenario is set up appropriately
	setupScenario();
}

// Shutdown
void SimulationManager::shutDown(void) {
}

/**************************************************************************************************
* Get the singleton instance
**************************************************************************************************/
SimulationManager& SimulationManager::getSingleton(void) { 
	assert(singleton);
	return *singleton;
}

/**************************************************************************************************
* Get the singleton pointer
**************************************************************************************************/
SimulationManager* SimulationManager::getSingletonPtr(void) {
	return singleton;
}

// Update
void SimulationManager::gameLoop(void) {
	MSG msg;	// Windows message
	BOOL done = FALSE;	// Bool variable to exit loop

	while( !done ) {
		// Is there a message waiting?
		if( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) {
			// Have we received a quit message?
			if( msg.message == WM_QUIT ) {
				done = TRUE;	// If so, we want to quit
			} else {
				// Otherwise deal with Windows messages
				TranslateMessage(&msg);	// Translate the message
				DispatchMessage(&msg);	// Dispatch the message
			}
		} else { // If there is no message
			// handle input
			inputManager->handleInput();

			// Run if the game is not paused
			if(!getGamePaused()) {
				// Update physics
				physicsManager->update();

				// Post-physics update
				// Define start and end of vector
				std::vector<Entity*>::iterator begin = entityList.begin();
				std::vector<Entity*>::iterator end = entityList.end();

				// Iterate through the list and update each object
				for(std::vector<Entity*>::iterator itr = begin; itr != end; ++itr) {
					// Apply update
					(*itr)->postPhysicsUpdate();
				}
			}

			// Draw the scene
			renderManager->renderScene();
		}
	}
}
 
/**************************************************************************************************
* Create the bar
**************************************************************************************************/
void SimulationManager::createBar(void) {
	// create its mesh
	Mesh* mesh = new Mesh(CUBE);
	RigidBody* body = new RigidBody(mesh->halfDimensions, 1.5f);

	// set the object's position/screen coordinates based on the vector passed in
	mesh->screenCoords = Vector3(-2.0f, 0.5f, -10.0f) + startPos;
	body->tPosition = Vector3(-2.0f, 0.5f, -10.0f) + startPos;

	// Move the origin for the shape
	for(unsigned int i = 0; i < mesh->vertices.size(); i++) {
		mesh->vertices[i].x += mesh->halfDimensions.x;
	}

	// Create the entity
	bar = new Entity(mesh, body);
	
	// Add the mesh to the list of objects to render
	renderManager->addToScene(mesh);

	// Add the body to the list of rigid bodies to update
	physicsManager->setBar(body);

	// Add to the list of entities
	entityList.push_back(bar);
}

/**************************************************************************************************
* Create the pivot point
**************************************************************************************************/
void SimulationManager::createPivot(void) {
	// Create its mesh
	Mesh* mesh = new Mesh(PYRAMID);
	mesh->screenCoords = Vector3(-2.0f, 0.0f, -10.0f) + startPos;

	// Create the pivot
	pivot = new Spring();
	pivot->mesh = mesh;
	pivot->anchorGlobal = mesh->screenCoords;
	pivot->anchorGlobal.y += mesh->halfDimensions.y;
	pivot->anchorLocal = Vector3(0.0f, 0.25f, 0.0f);
	pivot->naturalLength = Vector3(0.0f, -0.5f, 0.0f);
	pivot->currentLength = spring->naturalLength;
	pivot->springConstant = 1.2f;
	pivot->dampingConstant = 0.95f;

	// Add the mesh to the scene
	renderManager->addToScene(mesh);

	// Add the spring to physics
	physicsManager->setPivot(pivot->anchorGlobal);
}

/**************************************************************************************************
* Create the spring
**************************************************************************************************/
void SimulationManager::createSpring(void) {
	// Create its mesh
	Mesh* mesh = new Mesh(PYRAMID);
	mesh->screenCoords = Vector3(0.0f, 2.0f, -10.0f) + startPos;
	mesh->screenCoords.x += bar->getBody()->halfWidths.x;

	// Ivert y! It must be upside down. Pyrimidal logic.
	for(unsigned int i = 0; i < mesh->vertices.size(); i++) {
		mesh->vertices[i].y *= -1;
	}

	// Create the spring
	spring = new Spring();
	spring->mesh = mesh;
	spring->anchorGlobal = mesh->screenCoords;
	spring->anchorGlobal.y += mesh->halfDimensions.y;
	spring->anchorLocal = Vector3(0.0f, 0.25f, 0.0f);
	spring->naturalLength = Vector3(0.0f, -0.5f, 0.0f);
	spring->currentLength = spring->naturalLength;
	spring->springConstant = 7.2f;
	spring->dampingConstant = 0.9995f;

	// Add the mesh to the scene
	renderManager->addToScene(mesh);

	// Add the spring to physics
	physicsManager->setSpring(spring);
}

/**************************************************************************************************
* Make sure the scenario is set up appropriately
**************************************************************************************************/
void SimulationManager::setupScenario(void) {
	// Make sure that the spring is at the same level as the far anchor point
	spring->mobileAnchor = Vector3(2.0f, 0.5f, -10.0f);// + startPos;//>updatePositionGlobal(Vector3(2.0f, 0.5f, -10.0f) + startPos);
	spring->currentLength = spring->mobileAnchor - spring->anchorGlobal;

	spring->mesh->vertices[0] = spring->mobileAnchor - spring->anchorGlobal;
	spring->mesh->vertices[0].y += spring->mesh->halfDimensions.y * 2.0f;
	spring->mesh->vertices[3] = spring->mobileAnchor - spring->anchorGlobal;
	spring->mesh->vertices[3].y += spring->mesh->halfDimensions.y * 2.0f;
	spring->mesh->vertices[6] = spring->mobileAnchor - spring->anchorGlobal;
	spring->mesh->vertices[6].y += spring->mesh->halfDimensions.y * 2.0f;
	spring->mesh->vertices[9] = spring->mobileAnchor - spring->anchorGlobal;
	spring->mesh->vertices[9].y += spring->mesh->halfDimensions.y * 2.0f;
}

/**************************************************************************************************
* Get bar
**************************************************************************************************/
Entity* SimulationManager::getBar(void) { return bar; }
